OstreeSePolicy: add ostree_sepolicy_get_csum()
authorJonathan Lebon <jlebon@redhat.com>
Wed, 23 Mar 2016 19:54:49 +0000 (15:54 -0400)
committerColin Walters (automation) <walters+githubbot@verbum.org>
Fri, 25 Mar 2016 15:28:29 +0000 (15:28 +0000)
This can be used as a fingerprint to determine whether two
OstreeSePolicy objects are equivalent.

Also add documentation for ostree_sepolicy_get_name().

Closes: #219
Approved by: cgwalters

src/libostree/libostree.sym
src/libostree/ostree-sepolicy.c
src/libostree/ostree-sepolicy.h
src/libotutil/ot-checksum-utils.c
src/libotutil/ot-checksum-utils.h

index 47015e14f3f953248eb93a7b970202bf3df3f985..ca3c6fd09fa1bcd8624072f2e09cc0c261d5dd8e 100644 (file)
@@ -331,4 +331,5 @@ global:
 LIBOSTREE_2016.5 {
 global:
         ostree_repo_import_object_from_with_trust;
+        ostree_sepolicy_get_csum;
 } LIBOSTREE_2016.4;
index b8e3572de69ad4aa43255cdc6b0590604b38a480..3b1a391bc3c8f90855bdd92d9e000ea796a2511d 100644 (file)
@@ -50,6 +50,7 @@ struct OstreeSePolicy {
   GFile *selinux_policy_root;
   struct selabel_handle *selinux_hnd;
   char *selinux_policy_name;
+  char *selinux_policy_csum;
 #endif
 };
 
@@ -77,6 +78,7 @@ ostree_sepolicy_finalize (GObject *object)
 #ifdef HAVE_SELINUX
   g_clear_object (&self->selinux_policy_root);
   g_clear_pointer (&self->selinux_policy_name, g_free);
+  g_clear_pointer (&self->selinux_policy_csum, g_free);
   if (self->selinux_hnd)
     {
       selabel_close (self->selinux_hnd);
@@ -155,6 +157,93 @@ ostree_sepolicy_class_init (OstreeSePolicyClass *klass)
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 }
 
+#ifdef HAVE_SELINUX
+
+/* Find the latest policy file in our root and return its checksum. */
+static gboolean
+get_policy_checksum (char        **out_csum,
+                     GCancellable *cancellable,
+                     GError      **error)
+{
+  gboolean ret = FALSE;
+
+  const char *binary_policy_path = selinux_binary_policy_path ();
+  const char *binfile_prefix = glnx_basename (binary_policy_path);
+  g_autofree char *bindir_path = g_path_get_dirname (binary_policy_path);
+
+  glnx_fd_close int bindir_dfd = -1;
+
+  g_autofree char *best_policy = NULL;
+  int best_version = 0;
+
+  g_auto(GLnxDirFdIterator) dfd_iter = { 0,};
+
+  if (!glnx_opendirat (AT_FDCWD, bindir_path, TRUE, &bindir_dfd, error))
+    goto out;
+
+  if (!glnx_dirfd_iterator_init_at (bindir_dfd, ".", FALSE, &dfd_iter, error))
+    goto out;
+
+  while (TRUE)
+    {
+      struct dirent *dent = NULL;
+
+      if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent,
+                                                       cancellable, error))
+        goto out;
+
+      if (dent == NULL)
+        break;
+
+      if (dent->d_type == DT_REG)
+        {
+          /* We could probably save a few hundred nanoseconds if we accept that
+           * the prefix will always be "policy" and hardcode that in a static
+           * compile-once GRegex... But picture how exciting it'd be if it *did*
+           * somehow change; there would be cheers & slow-mo high-fives at the
+           * sight of our code not breaking. Is that hope not worth a fraction
+           * of a millisecond? I believe it is... or maybe I'm just lazy. */
+          g_autofree char *regex = g_strdup_printf ("^\\Q%s\\E\\.[0-9]+$",
+                                                    binfile_prefix);
+
+          /* we could use match groups to extract the version, but mehhh, we
+           * already have the prefix on hand */
+          if (g_regex_match_simple (regex, dent->d_name, 0, 0))
+            {
+              int version = /* do +1 for the period */
+                (int)g_ascii_strtoll (dent->d_name + strlen (binfile_prefix)+1,
+                                      NULL, 10);
+              g_assert (version > 0);
+
+              if (version > best_version)
+                {
+                  best_version = version;
+                  g_free (best_policy);
+                  best_policy = g_strdup (dent->d_name);
+                }
+            }
+        }
+    }
+
+  if (!best_policy)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Could not find binary policy file");
+      goto out;
+    }
+
+  *out_csum = ot_checksum_file_at (bindir_dfd, best_policy, G_CHECKSUM_SHA256,
+                                   cancellable, error);
+  if (*out_csum == NULL)
+    goto out;
+
+  ret = TRUE;
+out:
+  return ret;
+}
+
+#endif
+
 static gboolean
 initable_init (GInitable     *initable,
                GCancellable  *cancellable,
@@ -257,6 +346,12 @@ initable_init (GInitable     *initable,
         freecon (con);
       }
 
+      if (!get_policy_checksum (&self->selinux_policy_csum, cancellable, error))
+        {
+          g_prefix_error (error, "While calculating SELinux checksum: ");
+          goto out;
+        }
+
       self->selinux_policy_name = g_strdup (policytype);
       self->selinux_policy_root = g_object_ref (etc_selinux_dir);
     }
@@ -306,6 +401,12 @@ ostree_sepolicy_get_path (OstreeSePolicy  *self)
   return self->path;
 }
 
+/**
+ * ostree_sepolicy_get_name:
+ * @self:
+ *
+ * Returns: (transfer none): Type of current policy
+ */
 const char *
 ostree_sepolicy_get_name (OstreeSePolicy *self)
 {
@@ -316,6 +417,22 @@ ostree_sepolicy_get_name (OstreeSePolicy *self)
 #endif
 }
 
+/**
+ * ostree_sepolicy_get_csum:
+ * @self:
+ *
+ * Returns: (transfer none): Checksum of current policy
+ */
+const char *
+ostree_sepolicy_get_csum (OstreeSePolicy *self)
+{
+#ifdef HAVE_SELINUX
+  return self->selinux_policy_csum;
+#else
+  return NULL;
+#endif
+}
+
 /**
  * ostree_sepolicy_get_label:
  * @self: Self
index 83e3b379d87a5adf82a09572521d9e877d221909..d204953e80b771e5ac153f8bd0189f7c103f9b2e 100644 (file)
@@ -44,6 +44,9 @@ GFile * ostree_sepolicy_get_path (OstreeSePolicy  *self);
 _OSTREE_PUBLIC
 const char *ostree_sepolicy_get_name (OstreeSePolicy *self);
 
+_OSTREE_PUBLIC
+const char *ostree_sepolicy_get_csum (OstreeSePolicy *self);
+
 _OSTREE_PUBLIC
 gboolean ostree_sepolicy_get_label (OstreeSePolicy    *self,
                                     const char       *relpath,
index b2aaf3294fa28ec6889e909926f7a5b410188e06..8d30bdc30770b8aa3bd56f3f2095e1108595efab 100644 (file)
@@ -140,17 +140,17 @@ ot_gio_checksum_stream (GInputStream   *in,
 }
 
 char *
-ot_checksum_file (GFile          *file,
-                  GChecksumType   checksum_type,
-                  GCancellable   *cancellable,
-                  GError        **error)
+ot_checksum_file_at (int             dfd,
+                     const char     *path,
+                     GChecksumType   checksum_type,
+                     GCancellable   *cancellable,
+                     GError        **error)
 {
   GChecksum *checksum = NULL;
   char *ret = NULL;
   g_autoptr(GInputStream) in = NULL;
 
-  in = (GInputStream*)g_file_read (file, cancellable, error);
-  if (!in)
+  if (!ot_openat_read_stream (dfd, path, TRUE, &in, cancellable, error))
     goto out;
 
   checksum = g_checksum_new (checksum_type);
index eb8bbc04eef44109dab2b46570d8b910e250e917..8b3a394e492867f235e2f71b9799a994a6ec3793 100644 (file)
@@ -53,10 +53,11 @@ gboolean ot_gio_checksum_stream (GInputStream   *in,
                                  GCancellable   *cancellable,
                                  GError        **error);
 
-char * ot_checksum_file (GFile          *file,
-                         GChecksumType   checksum_type,
-                         GCancellable   *cancellable,
-                         GError        **error);
+char * ot_checksum_file_at (int             dfd,
+                            const char     *path,
+                            GChecksumType   checksum_type,
+                            GCancellable   *cancellable,
+                            GError        **error);
 
 void ot_gio_checksum_stream_async (GInputStream         *in,
                                    int                   io_priority,